﻿using System;
using System.ComponentModel.Composition;
using Cubicle.Core.Interfaces;

namespace Cubicle.Core.Processors.Parameter
{
    [Export("Cubicle.Processors.Parameter.DoubleArrayParameterResolver", typeof(IProcessor<ParameterRequest>))]
    [ExportMetadata("Name", "DoubleArrayParameterResolver")]
    [ExportMetadata("Description", "Resolves a parameter to a double array.")]
    public class DoubleArrayParameterResolver : ConditionalProcessor<ParameterRequest>
    {
        private readonly ParameterProcessor _parameterResolver;

        [ImportingConstructor]
        public DoubleArrayParameterResolver(ParameterProcessor parameterResolver)
        {
            _parameterResolver = parameterResolver;
        }

        #region Overrides of Processor<ParameterRequest>

        public override bool Condition(ParameterRequest request)
        {
            var valueType = request.SourceType;
            return valueType.IsArray
                   && valueType.GetElementType().IsArray
                   && request.TargetType.IsArray
                   && request.TargetType.GetArrayRank() == 2;
        }

        public override ParameterRequest ConditionalProcess(ParameterRequest request)
        {
            // Value will come in as a jagged array
            var doubleArray = JaggedToDouble(request.Source as Array);
            var elemType = request.TargetType.GetElementType();
            var dim0Length = doubleArray.GetLength(0);
            var dim1Length = doubleArray.GetLength(1);
            var result = Array.CreateInstance(elemType, new[] { dim0Length, dim1Length });
            for (var i = 0; i < dim0Length; i++)
                for (var j = 0; j < dim1Length; j++)
                {
                    var parRequest = new ParameterRequest(elemType, doubleArray.GetValue(i, j));
                    _parameterResolver.Process(parRequest);
                    result.SetValue(parRequest.Result, i, j);
                }

            request.Complete(result);
            return request;
        }

        private static object[,] JaggedToDouble(Array jagged)
        {
            // Declare result
            object[,] result;

            // Find length of second dimension
            int dimLength = 0;
            foreach (var item in jagged)
                dimLength = Math.Max(dimLength, ((Array)item).Length);

            // Initialise result
            result = new object[jagged.Length, dimLength];

            // Initialise the structure
            Array temp;
            for (int i = 0; i < jagged.Length; i++)
            {
                temp = jagged.GetValue(i) as Array;
                for (int j = 0; j < temp.Length; j++)
                    result[i, j] = temp.GetValue(j);
            }

            // Return converted result
            return result;
        }

        #endregion
    }
}